(function (global, factory) {
  // UMD 模块化封装（支持 CommonJS/AMD/全局变量）
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
      (global.MoveLine = factory());
}(this, (function () {
  'use strict';

  /**
   * Canvas 图层管理类
   * @constructor
   * @param {Object} options - 配置参数
   * @param {ol.Map} options.map - OpenLayers 地图实例
   */
  function CanvasLayer(options) {
    this.options = options || {};
    this.paneName = this.options.paneName || 'labelPane';  // 图层容器名称
    this.zIndex = this.options.zIndex || 0;   // 层级控制
    this._map = options.map;   // 绑定的地图实例
    this._lastDrawTime = null;
    this.show();
  }

  CanvasLayer.prototype.initialize = function () {
    // 创建画布并绑定到地图容器
    var canvas = this.canvas = document.createElement('canvas');
    var ctx = this.ctx = this.canvas.getContext('2d');
    canvas.style.cssText = 'position:absolute;' + 'left:0;' + 'top:0;' + 'z-index:' + this.zIndex + ';';
    this.adjustSize();
    this.adjustRatio(ctx);
    map.getViewport().appendChild(canvas);
    var that = this;

    // 监听地图事件实现画布动态调整
    map.getView().on('propertychange', function () {
      // $(canvas).hide();
      canvas.style.display = 'none';   // 地图属性变化时隐藏
    });

    // 地图移动结束后重绘
    map.on("moveend", function () {
      // $(canvas).show();
      canvas.style.display = 'block';
      that.adjustSize();
      that._draw();
    });
    return this.canvas;
  };

  CanvasLayer.prototype.adjustSize = function () {
    var size = this._map.getSize();
    var canvas = this.canvas;
    canvas.width = size[0];
    canvas.height = size[1];
    canvas.style.width = canvas.width + 'px';
    canvas.style.height = canvas.height + 'px';
  };

  CanvasLayer.prototype.adjustRatio = function (ctx) {
    var backingStore = ctx.backingStorePixelRatio || ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
    var pixelRatio = (window.devicePixelRatio || 1) / backingStore;
    var canvasWidth = ctx.canvas.width;
    var canvasHeight = ctx.canvas.height;
    ctx.canvas.width = canvasWidth * pixelRatio;
    ctx.canvas.height = canvasHeight * pixelRatio;
    ctx.canvas.style.width = canvasWidth + 'px';
    ctx.canvas.style.height = canvasHeight + 'px';
    ctx.scale(pixelRatio, pixelRatio);
  };

  CanvasLayer.prototype.draw = function () {
    var self = this;
    var args = arguments;
    clearTimeout(self.timeoutID);
    self.timeoutID = setTimeout(function () {
      self._draw();
    }, 15);
  };

  CanvasLayer.prototype._draw = function () {
    var map = this._map;
    var size = map.getSize();
    var center = map.getView().getCenter();
    if (center) {
      var pixel = map.getPixelFromCoordinate(center);
      this.canvas.style.left = pixel[0] - size[0] / 2 + 'px';
      this.canvas.style.top = pixel[1] - size[1] / 2 + 'px';
      this.options.update && this.options.update.call(this);
    }
  };

  CanvasLayer.prototype.getContainer = function () {
    return this.canvas;
  };

  CanvasLayer.prototype.show = function () {
    this.initialize();
    this.canvas.style.display = 'block';
  };

  CanvasLayer.prototype.hide = function () {
    this.canvas.style.display = 'none';
  };

  CanvasLayer.prototype.setZIndex = function (zIndex) {
    this.canvas.style.zIndex = zIndex;
  };

  CanvasLayer.prototype.getZIndex = function () {
    return this.zIndex;
  };

  var global = typeof window === 'undefined' ? {} : window;

  var requestAnimationFrame = global.requestAnimationFrame || global.mozRequestAnimationFrame || global.webkitRequestAnimationFrame || global.msRequestAnimationFrame || function (callback) {
    return global.setTimeout(callback, 1000 / 60);
  };

  /**
   * 路径动画主类
   * @constructor
   * @param {ol.Map} map - OpenLayers 地图实例
   * @param {Object} userOptions - 用户配置
   */
  var MoveLine = function MoveLine(map, userOptions) {
    const OPTIMIZE = {
      MAX_LINES: 200,           // 最大同时渲染路径数
      TRAIL_LENGTH: 0.92,       // 轨迹拖尾透明度
      FRAME_THROTTLE: 2         // 帧率控制
    };

    var self = this;

    // 默认配置（包含样式参数）
    var options = {
      // marker点半径
      markerRadius: 3,
      // marker点颜色,为空或null则默认取线条颜色
      markerColor: '#fff',
      // 线条类型 solid、dashed、dotted
      lineType: 'solid',
      // 线条宽度
      lineWidth: 1,
      // 线条颜色
      colors: ['#F9815C', '#F8AB60', '#EDCC72', '#E2F194', '#94E08A', '#4ECDA5'],
      // 移动点半径
      moveRadius: 2,
      // 移动点颜色
      fillColor: '#fff',
      // 移动点阴影颜色
      shadowColor: '#fff',
      // 移动点阴影大小
      shadowBlur: 5
    };

    // 全局变量
    var baseLayer = null,
      animationLayer = null,
      width = map.getSize()[0],
      height = map.getSize()[1],
      animationFlag = true,
      markLines = [];

    // 参数合并
    var merge = function merge(userOptions, options) {
      Object.keys(userOptions).forEach(function (key) {
        options[key] = userOptions[key];
      });
    };

    // 标记点类（起点/终点）
    function Marker(opts) {
      this.city = opts.city;
      this.location = opts.location;  // 经纬度坐标
      this.color = opts.color;        // 颜色配置
    }

    Marker.prototype.draw = function (context) {
      var pixel = this.pixel = map.getPixelFromCoordinate(this.location);

      context.save();
      context.beginPath();
      context.fillStyle = options.markerColor || this.color;
      context.arc(pixel[0], pixel[1], options.markerRadius, 0, Math.PI * 2, true);
      context.closePath();
      context.fill();

      context.textAlign = 'center';
      context.textBaseline = 'middle';
      context.font = '12px Microsoft YaHei';
      context.fillStyle = this.color;
      context.fillText(this.city, pixel[0], pixel[1] - 10);
      context.restore();
    };

    // 路径线类（含动画逻辑）
    function MarkLine(opts) {
      this.from = opts.from;
      this.to = opts.to;
      this.id = opts.id;
      this.step = 0;   // 动画步进计数器
    }

    // 生成贝塞尔曲线路径点
    MarkLine.prototype.getPointList = function (from, to) {
      // 使用三次贝塞尔曲线生成平滑路径
      var points = [[from[0], from[1]], [to[0], to[1]]];
      var ex = points[1][0];
      var ey = points[1][1];
      points[3] = [ex, ey];
      points[1] = this.getOffsetPoint(points[0], points[3]);
      points[2] = this.getOffsetPoint(points[3], points[0]);
      points = this.smoothSpline(points, false);
      // 修正最后一点在插值产生的偏移
      points[points.length - 1] = [ex, ey];
      return points;
    };

    MarkLine.prototype.getOffsetPoint = function (start, end) {
      var distance = this.getDistance(start, end) / 3; //除以3？
      var angle, dX, dY;
      var mp = [start[0], start[1]];
      var deltaAngle = -0.2; //偏移0.2弧度
      if (start[0] != end[0] && start[1] != end[1]) {
        //斜率存在
        var k = (end[1] - start[1]) / (end[0] - start[0]);
        angle = Math.atan(k);
      } else if (start[0] == end[0]) {
        //垂直线
        angle = (start[1] <= end[1] ? 1 : -1) * Math.PI / 2;
      } else {
        //水平线
        angle = 0;
      }
      if (start[0] <= end[0]) {
        angle -= deltaAngle;
        dX = Math.round(Math.cos(angle) * distance);
        dY = Math.round(Math.sin(angle) * distance);
        mp[0] += dX;
        mp[1] += dY;
      } else {
        angle += deltaAngle;
        dX = Math.round(Math.cos(angle) * distance);
        dY = Math.round(Math.sin(angle) * distance);
        mp[0] -= dX;
        mp[1] -= dY;
      }
      return mp;
    };

    MarkLine.prototype.smoothSpline = function (points, isLoop) {
      var len = points.length;
      var ret = [];
      var distance = 0;
      for (var i = 1; i < len; i++) {
        distance += this.getDistance(points[i - 1], points[i]);
      }
      var segs = distance / 2;
      segs = segs < len ? len : segs;
      for (var i = 0; i < segs; i++) {
        var pos = i / (segs - 1) * (isLoop ? len : len - 1);
        var idx = Math.floor(pos);
        var w = pos - idx;
        var p0;
        var p1 = points[idx % len];
        var p2;
        var p3;
        if (!isLoop) {
          p0 = points[idx === 0 ? idx : idx - 1];
          p2 = points[idx > len - 2 ? len - 1 : idx + 1];
          p3 = points[idx > len - 3 ? len - 1 : idx + 2];
        } else {
          p0 = points[(idx - 1 + len) % len];
          p2 = points[(idx + 1) % len];
          p3 = points[(idx + 2) % len];
        }
        var w2 = w * w;
        var w3 = w * w2;

        ret.push([this.interpolate(p0[0], p1[0], p2[0], p3[0], w, w2, w3), this.interpolate(p0[1], p1[1], p2[1], p3[1], w, w2, w3)]);
      }
      return ret;
    };

    MarkLine.prototype.interpolate = function (p0, p1, p2, p3, t, t2, t3) {
      var v0 = (p2 - p0) * 0.5;
      var v1 = (p3 - p1) * 0.5;
      return (2 * (p1 - p2) + v0 + v1) * t3 + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 + v0 * t + p1;
    };

    MarkLine.prototype.getDistance = function (p1, p2) {
      return Math.sqrt((p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]));
    };

    MarkLine.prototype.drawMarker = function (context) {
      this.from.draw(context);
      this.to.draw(context);
    };

    // 路径绘制方法
    MarkLine.prototype.drawLinePath = function (context) {
      // 根据 lineType 绘制实线/虚线
      var pointList = this.path = this.getPointList(map.getPixelFromCoordinate(this.from.location), map.getPixelFromCoordinate(this.to.location));
      var len = pointList.length;
      context.save();
      context.beginPath();
      context.lineWidth = options.lineWidth;
      context.strokeStyle = options.colors[this.id];

      if (!options.lineType || options.lineType == 'solid') {
        context.moveTo(pointList[0][0], pointList[0][1]);
        for (var i = 0; i < len; i++) {
          context.lineTo(pointList[i][0], pointList[i][1]);
        }
      } else if (options.lineType == 'dashed' || options.lineType == 'dotted') {
        for (var i = 1; i < len; i += 2) {
          context.moveTo(pointList[i - 1][0], pointList[i - 1][1]);
          context.lineTo(pointList[i][0], pointList[i][1]);
        }
      }
      context.stroke();
      context.restore();
      this.step = 0; // 缩放地图时重新绘制动画
    };

    // 移动圆点动画方法
    MarkLine.prototype.drawMoveCircle = function (context) {
      // 使用 step 控制动画进度
      var pointList = this.path || this.getPointList(map.getPixelFromCoordinate(this.from.location), map.getPixelFromCoordinate(this.to.location));

      context.save();
      context.fillStyle = options.fillColor;
      context.shadowColor = options.shadowColor;
      context.shadowBlur = options.shadowBlur;
      context.beginPath();
      context.arc(pointList[this.step][0], pointList[this.step][1], options.moveRadius, 0, Math.PI * 2, true);
      context.fill();
      context.closePath();
      context.restore();
      this.step += 1;
      if (this.step >= pointList.length) {
        this.step = 0;
      }
    };

    // 底层canvas渲染，标注，线条  双画布渲染架构
    var brush = function brush() {   // 底层：静态元素（标记+路径）
      var baseCtx = baseLayer.canvas.getContext('2d');
      if (!baseCtx) {
        return;
      }

      addMarkLine();

      baseCtx.clearRect(0, 0, width, height);

      markLines.forEach(function (line) {
        line.drawMarker(baseCtx);
        line.drawLinePath(baseCtx);
      });
    };

    // 修改渲染方法
    let frameCount = 0;
    // 上层canvas渲染，动画效果
    var render = function render() {  // 上层：动画元素（移动圆点）
      if (frameCount++ % OPTIMIZE.FRAME_THROTTLE !== 0) return;

      // 使用 destination-in 合成模式实现轨迹拖尾效果
      var animationCtx = animationLayer.canvas.getContext('2d');
      if (!animationCtx) {
        return;
      }

      if (!animationFlag) {
        animationCtx.clearRect(0, 0, width, height);
        return;
      }

      var prev = animationCtx.globalCompositeOperation;
      // 通过合成模式实现轨迹拖尾效果
      animationCtx.globalCompositeOperation = 'destination-in';
      animationCtx.fillStyle = `rgba(0,0,0,${OPTIMIZE.TRAIL_LENGTH})`; // 透明度控制拖尾长度
      animationCtx.fillRect(0, 0, width, height);
      animationCtx.globalCompositeOperation = prev;

      for (var i = 0; i < markLines.length; i++) {
        var markLine = markLines[i];
        markLine.drawMoveCircle(animationCtx); //移动圆点
      }
    };
    var addMarkLine = function addMarkLine() {
      markLines = [];
      var dataset = options.data;
      dataset.forEach(function (line, i) {
        markLines.push(new MarkLine({
          id: i,
          from: new Marker({
            city: line.from.city,
            location: [line.from.lnglat[0], line.from.lnglat[1]],
            color: options.colors[i]
          }),
          to: new Marker({
            city: line.to.city,
            location: [line.to.lnglat[0], line.to.lnglat[1]],
            color: options.colors[i]
          })
        }));
      });
    };

    //初始化
    var init = function init(map, options) {
      merge(userOptions, options);

      baseLayer = new CanvasLayer({
        map: map,
        update: brush
      });

      animationLayer = new CanvasLayer({
        map: map,
        update: render
      });

      // 动画循环核心
      (function drawFrame() {
        requestAnimationFrame(drawFrame);  // 使用 RAF 优化性能
        render();
      })();
    };

    init(map, options);

    self.options = options;
  };

  // 暴露公共 API
  MoveLine.prototype.update = function (resetOpts) {
    // 支持动态更新配置
    for (var key in resetOpts) {
      this.options[key] = resetOpts[key];
    }
  };

  return MoveLine;

})));